﻿Public Class GemValidityMatrix
    ' I don't want to think about the horror that is this,
    ' but I think that calculating and populating this 
    ' prior to spawning any threads is going to be
    ' more beneficial than trying to spawn it all later.
    Implements ICloneable

    ' index order: gemID, hitvalue, expvalue, socketcolor, value
    Private theMatrix(,,,) As Boolean
    Private numGems As Integer
    Private maxHit As Integer
    Private maxExp As Integer
    Private meleeHitCap As Integer
    Private forceHit As forceHitCap
    Private forceExp As Boolean

    Enum socketcolor As Integer
        none
        red
        yellow
        blue
        prismatic
    End Enum

    Enum forceHitCap As Integer
        none
        melee
        spell
    End Enum

    Public Function Clone() Implements System.ICloneable.Clone
        Try
            Return New GemValidityMatrix With {.theMatrix = theMatrix.Clone, .numGems = numGems, .maxHit = maxHit, .maxExp = maxExp, .meleeHitCap = meleeHitCap}
        Catch ex As Exception
            MessageBox.Show("An unexpected error has occurred. DK Optimize will exit.", "DK Optimize", MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1)
            ErrLogWriter.writeLog(ex)
            End
        End Try
    End Function

    Public Sub setupMatrix(ByVal numGemsIn As Integer, ByVal maxHitIn As Integer, ByVal maxExpIn As Integer, ByVal MeleeHitCapIn As Integer, ByVal forceHitIN As forceHitCap, ByVal forceExpIN As Boolean)
        ' Dimension the array
        Try
            numGems = numGemsIn
            maxHit = maxHitIn
            maxExp = maxExpIn
            meleeHitCap = MeleeHitCapIn
            ReDim theMatrix(0 To numGems - 1, 0 To maxHit, 0 To maxExp, 0 To 4)
            forceHit = forceHitIN
            forceExp = forceExpIN
        Catch ex As Exception
            MessageBox.Show("An unexpected error has occurred. DK Optimize will exit.", "DK Optimize", MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1)
            ErrLogWriter.writeLog(ex)
            End
        End Try
    End Sub

    Public Sub determineOptimal(ByVal gems() As Gem, ByVal weights As Weight)
        ' For each combination of hit and expertise determine what gems are
        ' possibly optimal in what sockets.
        ' for every level of hit and expertise, find the best matching gem for each socket 
        ' and mark it as valid.
        ' we also need to mark the best gem for the socket that doesn't have hit or expertise on it
        ' since these need to be valid.  PROOF:
        '
        ' given in order a red socket and a yellow socket, both with 4AP bonuses.
        ' assume we're 10 hit short of capping hit, after which it has zero value.
        ' we hit the red socket, and the best is orange 10/10 gem since hit > str.
        ' that goes in and we're capped on hit. and we get the set bonus of 4 AP
        ' 
        ' now the yellow socket comes and 10/10hit gem is no longer best.  our options are
        ' either str or str/crit (or haste) for the socket bonus.  either way we either lose the socket bonus
        ' or the value of the 10 str - 10 crit.
        '
        ' the best solution is actually red in the red socket and 10/10hit in the yellow socket, which gives full
        ' str and both bonuses.
        '


        ' then find the best gem overall and mark it as valid for all socket colors.
        ' We're going to do JCing the naive way since it really just makes life hard, so
        ' don't worry about Dragon's Eyes.
        Try
            Dim hitLoopvar As Integer
            Dim expLoopvar As Integer
            Dim gemLoopvar As Integer
            Dim socketLoopvar As Integer

            ' First let's clear everything.
            For gemLoopvar = 0 To numGems - 1
                For hitLoopvar = 0 To maxHit
                    For expLoopvar = 0 To maxExp
                        For socketLoopvar = 0 To 4
                            theMatrix(gemLoopvar, hitLoopvar, expLoopvar, socketLoopvar) = False
                        Next
                    Next
                Next
            Next

            ' Now the actual work.
            Dim tMaxScore As Double
            Dim tMaxNoCapsScore As Double
            Dim tGemScore As Double
            Dim offColorMax As Double
            Dim offColorMaxIndex As Double
            Dim bestHitGemScore As Double
            Dim bestHitGemIndex As Integer
            Dim bestExpGemScore As Double
            Dim bestExpGemIndex As Integer
            Dim accurateAmetrineIndex As Integer
            Dim BoldIndex As Integer
            Dim bestGemIndex(0 To 2, 0 To 2) As Integer '(hit, exp)
            Dim bestGemScore(0 To 2, 0 To 2) As Double
            Dim bestOffGemIndex(0 To 2, 0 To 2) As Integer
            Dim bestoffGemScore(0 To 2, 0 To 2) As Double
            Dim loopvar As Integer
            Dim loopvar2 As Integer

            BoldIndex = -1
            For loopvar = 0 To numGems - 1
                If gems(loopvar).str = 20 Then
                    BoldIndex = loopvar
                End If
            Next

            For hitLoopvar = 0 To maxHit
                For expLoopvar = 0 To maxExp
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            bestOffGemIndex(loopvar, loopvar2) = -1
                            bestoffGemScore(loopvar, loopvar2) = 0
                        Next
                    Next
                    offColorMax = 0
                    offColorMaxIndex = 0
                    ' Find the best red / purple / orange
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            bestGemIndex(loopvar, loopvar2) = -1
                            bestGemScore(loopvar, loopvar2) = 0
                        Next
                    Next
                    tMaxScore = 0
                    tMaxNoCapsScore = 0
                    bestHitGemIndex = -1
                    bestHitGemScore = 0
                    bestExpGemIndex = -1
                    bestExpGemScore = 0
                    accurateAmetrineIndex = -1
                    For gemLoopvar = 0 To numGems - 1
                        If gems(gemLoopvar).color = Gem.colors.red Or gems(gemLoopvar).color = Gem.colors.purple Or gems(gemLoopvar).color = Gem.colors.orange Then
                            tGemScore = gems(gemLoopvar).str * weights.str + gems(gemLoopvar).ap * weights.ap + gems(gemLoopvar).crit * weights.crit + gems(gemLoopvar).haste * weights.haste + gems(gemLoopvar).arp * weights.arp + gems(gemLoopvar).agi * weights.agi
                            'hit
                            If hitLoopvar + gems(gemLoopvar).hit <= meleeHitCap Then
                                tGemScore += weights.hit * gems(gemLoopvar).hit
                            Else
                                If hitLoopvar < meleeHitCap Then
                                    tGemScore += weights.hit * (meleeHitCap - hitLoopvar)
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - meleeHitCap) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - meleeHitCap) * weights.sphit
                                    End If
                                Else
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - hitLoopvar) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += gems(gemLoopvar).hit * weights.sphit
                                    End If
                                End If
                            End If
                            'exp
                            If expLoopvar + gems(gemLoopvar).exp <= maxExp Then
                                tGemScore += gems(gemLoopvar).exp * weights.exp
                            Else
                                If expLoopvar < maxExp Then
                                    tGemScore += (maxExp - expLoopvar) * weights.exp
                                Else
                                    tGemScore += 0
                                End If
                            End If
                            ' Now we have the full score of the gem
                            ' so figure out if it is the best
                            ' gem for what circumstances.
                            ' various hit / exp gems are listed separately if hit > best red stat
                            ' since then we need to get the 10/10 and the 0/20 gems.
                            Select Case gems(gemLoopvar).hit
                                Case 0
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(0, 0) Then
                                                bestGemScore(0, 0) = tGemScore
                                                bestGemIndex(0, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(0, 1) Then
                                                bestGemScore(0, 1) = tGemScore
                                                bestGemIndex(0, 1) = gemLoopvar
                                            End If
                                        Case 20
                                            If tGemScore > bestGemScore(0, 2) Then
                                                bestGemScore(0, 2) = tGemScore
                                                bestGemIndex(0, 2) = gemLoopvar
                                            End If
                                    End Select
                                Case 10
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(1, 0) Then
                                                bestGemScore(1, 0) = tGemScore
                                                bestGemIndex(1, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(1, 1) Then
                                                bestGemScore(1, 1) = tGemScore
                                                bestGemIndex(1, 1) = gemLoopvar
                                            End If
                                    End Select
                                Case 20
                                    If tGemScore > bestGemScore(2, 0) Then
                                        bestGemScore(2, 0) = tGemScore
                                        bestGemIndex(2, 0) = gemLoopvar
                                    End If
                            End Select

                            'If tGemScore > tMaxScore Then
                            '    tMaxScore = tGemScore
                            '    tMaxIndex = gemLoopvar
                            'End If
                            'If tGemScore > tMaxNoCapsScore And gems(gemLoopvar).exp = 0 And gems(gemLoopvar).hit = 0 Then
                            '    tMaxNoCapsScore = tGemScore
                            '    tMaxNoCapsIndex = gemLoopvar
                            'End If
                            'If tGemScore > offColorMax Then
                            '    offColorMax = tGemScore
                            '    offColorMaxIndex = gemLoopvar
                            'End If
                            '' for hit and exp cap forcing.  These aren't reset
                            '' for each socket
                            'If tGemScore > bestHitGemScore And gems(gemLoopvar).hit > 0 Then
                            '    bestHitGemScore = tGemScore
                            '    bestHitGemIndex = gemLoopvar
                            'End If
                            'If tGemScore > bestExpGemScore And gems(gemLoopvar).exp > 0 Then
                            '    bestExpGemScore = tGemScore
                            '    bestExpGemIndex = gemLoopvar
                            'End If
                        End If
                    Next
                    ' Ok, so now for red sockets I've got the best scoring gem of each hit / exp combination
                    ' stored in bestgemscore and bestgemindex arrays.  If hit cap isn't forced, then we select
                    ' gems as follows: for hit - if the best 20 hit is > best 20 nonhit, then take the 
                    ' 20 hit, 10 hit, and 20 nonhit.  If it's < 20 nonhit, ignore it and move ot the 10 hit
                    ' if that's > 20 nonhit, take 10 hit and 20 nonhit.
                    ' For exp, similar deal.
                    ' This is for the red slot, so ignoring capping restrictions, we only take the 10/10 hit /exp
                    ' gem if it is greater than the 0hit 20 noncap gem.

                    ' Hit
                    If bestGemScore(2, 0) > bestGemScore(0, 0) Then ' 20 hit gem
                        theMatrix(bestGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If
                    If bestGemScore(1, 0) > bestGemScore(0, 0) Then ' 10 hit / 10 noncap gem
                        theMatrix(bestGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If '
                    If bestGemIndex(0, 0) <> -1 Then
                        theMatrix(bestGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.red) = True ' 20 noncap gem
                    End If
                    If BoldIndex <> -1 Then
                        theMatrix(BoldIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If
                    ' note that the best noncapping stat gem of the appropriate color has no condition
                    ' associated with it.  This will always be good.

                    ' Exp
                    If bestGemScore(0, 2) > bestGemScore(0, 0) Then ' 20 exp gem
                        theMatrix(bestGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If
                    If bestGemScore(0, 1) > bestGemScore(0, 0) Then ' 10 exp gem
                        theMatrix(bestGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If

                    ' Exp / Hit
                    If bestGemScore(1, 1) > bestGemScore(0, 0) Then '10/10 gem
                        theMatrix(bestGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.red) = True
                    End If

                    ' Okay, now we've added the best on color gems for each of our hit / exp combinations
                    ' assuming that they were better than the best non-cappable gem. keep track of
                    ' the best overall gem of each category for off-color gemming later.
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            If bestGemScore(loopvar, loopvar2) > bestoffGemScore(loopvar, loopvar2) Then
                                bestoffGemScore(loopvar, loopvar2) = bestGemScore(loopvar, loopvar2)
                                bestOffGemIndex(loopvar, loopvar2) = bestGemIndex(loopvar, loopvar2)
                            End If
                        Next
                    Next



                    ' Find the best yellow / orange / green
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            bestGemIndex(loopvar, loopvar2) = -1
                            bestGemScore(loopvar, loopvar2) = 0
                        Next
                    Next
                    tMaxScore = 0
                    tMaxNoCapsScore = 0
                    For gemLoopvar = 0 To numGems - 1
                        If gems(gemLoopvar).color = Gem.colors.yellow Or gems(gemLoopvar).color = Gem.colors.orange Or gems(gemLoopvar).color = Gem.colors.green Then
                            tGemScore = gems(gemLoopvar).str * weights.str + gems(gemLoopvar).ap * weights.ap + gems(gemLoopvar).crit * weights.crit + gems(gemLoopvar).haste * weights.haste + gems(gemLoopvar).arp * weights.arp + gems(gemLoopvar).agi * weights.agi
                            'hit
                            If hitLoopvar + gems(gemLoopvar).hit <= meleeHitCap Then
                                tGemScore += weights.hit * gems(gemLoopvar).hit
                            Else
                                If hitLoopvar < meleeHitCap Then
                                    tGemScore += weights.hit * (meleeHitCap - hitLoopvar)
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - meleeHitCap) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - meleeHitCap) * weights.sphit
                                    End If
                                Else
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - hitLoopvar) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += gems(gemLoopvar).hit * weights.sphit
                                    End If
                                End If
                            End If
                            'exp
                            If expLoopvar + gems(gemLoopvar).exp <= maxExp Then
                                tGemScore += gems(gemLoopvar).exp * weights.exp
                            Else
                                If expLoopvar < maxExp Then
                                    tGemScore += (maxExp - expLoopvar) * weights.exp
                                Else
                                    tGemScore += 0
                                End If
                            End If
                            ' Now we have the full score of the gem
                            ' so figure out if it is the best
                            ' gem for what circumstances.
                            ' various hit / exp gems are listed separately if hit > best red stat
                            ' since then we need to get the 10/10 and the 0/20 gems.
                            Select Case gems(gemLoopvar).hit
                                Case 0
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(0, 0) Then
                                                bestGemScore(0, 0) = tGemScore
                                                bestGemIndex(0, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(0, 1) Then
                                                bestGemScore(0, 1) = tGemScore
                                                bestGemIndex(0, 1) = gemLoopvar
                                            End If
                                        Case 20
                                            If tGemScore > bestGemScore(0, 2) Then
                                                bestGemScore(0, 2) = tGemScore
                                                bestGemIndex(0, 2) = gemLoopvar
                                            End If
                                    End Select
                                Case 10
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(1, 0) Then
                                                bestGemScore(1, 0) = tGemScore
                                                bestGemIndex(1, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(1, 1) Then
                                                bestGemScore(1, 1) = tGemScore
                                                bestGemIndex(1, 1) = gemLoopvar
                                            End If
                                    End Select
                                Case 20
                                    If tGemScore > bestGemScore(2, 0) Then
                                        bestGemScore(2, 0) = tGemScore
                                        bestGemIndex(2, 0) = gemLoopvar
                                    End If
                            End Select
                            'If tGemScore > tMaxScore Then
                            '    tMaxScore = tGemScore
                            '    tMaxIndex = gemLoopvar
                            'End If
                            'If tGemScore > tMaxNoCapsScore And gems(gemLoopvar).exp = 0 And gems(gemLoopvar).hit = 0 Then
                            '    tMaxNoCapsScore = tGemScore
                            '    tMaxNoCapsIndex = gemLoopvar
                            'End If
                            'If tGemScore > offColorMax Then
                            '    offColorMax = tGemScore
                            '    offColorMaxIndex = gemLoopvar
                            'End If
                            '' for hit and exp cap forcing.  These aren't reset
                            '' for each socket
                            'If tGemScore > bestHitGemScore And gems(gemLoopvar).hit > 0 Then
                            '    bestHitGemScore = tGemScore
                            '    bestHitGemIndex = gemLoopvar
                            'End If
                            'If tGemScore > bestExpGemScore And gems(gemLoopvar).exp > 0 Then
                            '    bestExpGemScore = tGemScore
                            '    bestExpGemIndex = gemLoopvar
                            'End If
                            'If gems(gemLoopvar).hit <> 0 And gems(gemLoopvar).exp <> 0 Then
                            '    accurateAmetrineIndex = gemLoopvar
                            'End If
                        End If
                    Next


                    ' Ok, so now for yellow sockets I've got the best scoring gem of each hit / exp combination
                    ' stored in bestgemscore and bestgemindex arrays.  If hit cap isn't forced, then we select
                    ' gems as follows: for hit - if the best 20 hit is > best 20 nonhit, then take the 
                    ' 20 hit, 10 hit, and 20 nonhit.  If it's < 20 nonhit, ignore it and move ot the 10 hit
                    ' if that's > 20 nonhit, take 10 hit and 20 nonhit.
                    ' For exp, similar deal.
                    ' This is for the yellow slot, so ignoring capping restrictions, we only take the 10/10 hit /exp
                    ' gem if it is greater than the 10hit 10 noncap gem.

                    ' Hit
                    If bestGemScore(2, 0) > bestGemScore(0, 0) Then ' 20 hit gem
                        theMatrix(bestGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    End If
                    If bestGemScore(1, 0) > bestGemScore(0, 0) Then ' 10 hit / 10 noncap gem
                        theMatrix(bestGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    End If
                    If bestGemIndex(0, 0) <> -1 Then
                        theMatrix(bestGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True ' 20 noncap gem
                    End If
                    ' note that the best noncapping stat gem of the appropriate color has no condition
                    ' associated with it.  This will always be good.

                    ' Exp
                    If bestGemScore(0, 2) > bestGemScore(0, 0) Then ' 20 exp gem
                        theMatrix(bestGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    End If
                    If bestGemScore(0, 1) > bestGemScore(0, 0) Then ' 10 exp gem
                        theMatrix(bestGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    End If

                    ' Exp / Hit
                    If bestGemScore(1, 1) > bestGemScore(1, 0) Then '10/10 gem
                        theMatrix(bestGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    End If

                    ' Okay, now we've added the best on color gems for each of our hit / exp combinations
                    ' assuming that they were better than the best non-cappable gem. keep track of
                    ' the best overall gem of each category for off-color gemming later.
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            If bestGemScore(loopvar, loopvar2) > bestoffGemScore(loopvar, loopvar2) Then
                                bestoffGemScore(loopvar, loopvar2) = bestGemScore(loopvar, loopvar2)
                                bestOffGemIndex(loopvar, loopvar2) = bestGemIndex(loopvar, loopvar2)
                            End If
                        Next
                    Next

                    ' Find the best blue / purple / green
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            bestGemIndex(loopvar, loopvar2) = -1
                            bestGemScore(loopvar, loopvar2) = 0
                        Next
                    Next
                    tMaxScore = 0
                    tMaxNoCapsScore = 0
                    For gemLoopvar = 0 To numGems - 1
                        If gems(gemLoopvar).color = Gem.colors.blue Or gems(gemLoopvar).color = Gem.colors.purple Or gems(gemLoopvar).color = Gem.colors.green Then
                            tGemScore = gems(gemLoopvar).str * weights.str + gems(gemLoopvar).ap * weights.ap + gems(gemLoopvar).crit * weights.crit + gems(gemLoopvar).haste * weights.haste + gems(gemLoopvar).arp * weights.arp + gems(gemLoopvar).agi * weights.agi
                            'hit
                            If hitLoopvar + gems(gemLoopvar).hit <= meleeHitCap Then
                                tGemScore += weights.hit * gems(gemLoopvar).hit
                            Else
                                If hitLoopvar < meleeHitCap Then
                                    tGemScore += weights.hit * (meleeHitCap - hitLoopvar)
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - meleeHitCap) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - meleeHitCap) * weights.sphit
                                    End If
                                Else
                                    If hitLoopvar + gems(gemLoopvar).hit > maxHit Then
                                        tGemScore += (maxHit - hitLoopvar) * weights.sphit
                                        tGemScore += ((hitLoopvar + gems(gemLoopvar).hit) - maxHit) * weights.exhit
                                    Else
                                        tGemScore += gems(gemLoopvar).hit * weights.sphit
                                    End If
                                End If
                            End If
                            'exp
                            If expLoopvar + gems(gemLoopvar).exp <= maxExp Then
                                tGemScore += gems(gemLoopvar).exp * weights.exp
                            Else
                                If expLoopvar < maxExp Then
                                    tGemScore += (maxExp - expLoopvar) * weights.exp
                                Else
                                    tGemScore += 0
                                End If
                            End If
                            ' Now we have the full score of the gem
                            ' so figure out if it is the best
                            ' gem for what circumstances.
                            ' various hit / exp gems are listed separately if hit > best red stat
                            ' since then we need to get the 10/10 and the 0/20 gems.
                            Select Case gems(gemLoopvar).hit
                                Case 0
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(0, 0) Then
                                                bestGemScore(0, 0) = tGemScore
                                                bestGemIndex(0, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(0, 1) Then
                                                bestGemScore(0, 1) = tGemScore
                                                bestGemIndex(0, 1) = gemLoopvar
                                            End If
                                        Case 20
                                            If tGemScore > bestGemScore(0, 2) Then
                                                bestGemScore(0, 2) = tGemScore
                                                bestGemIndex(0, 2) = gemLoopvar
                                            End If
                                    End Select
                                Case 10
                                    Select Case gems(gemLoopvar).exp
                                        Case 0
                                            If tGemScore > bestGemScore(1, 0) Then
                                                bestGemScore(1, 0) = tGemScore
                                                bestGemIndex(1, 0) = gemLoopvar
                                            End If
                                        Case 10
                                            If tGemScore > bestGemScore(1, 1) Then
                                                bestGemScore(1, 1) = tGemScore
                                                bestGemIndex(1, 1) = gemLoopvar
                                            End If
                                    End Select
                                Case 20
                                    If tGemScore > bestGemScore(2, 0) Then
                                        bestGemScore(2, 0) = tGemScore
                                        bestGemIndex(2, 0) = gemLoopvar
                                    End If
                            End Select
                            'If tGemScore > tMaxScore Then
                            '    tMaxScore = tGemScore
                            '    tMaxIndex = gemLoopvar
                            'End If
                            'If tGemScore > tMaxNoCapsScore And gems(gemLoopvar).exp = 0 And gems(gemLoopvar).hit = 0 Then
                            '    tMaxNoCapsScore = tGemScore
                            '    tMaxNoCapsIndex = gemLoopvar
                            'End If
                            'If tGemScore > offColorMax Then
                            '    offColorMax = tGemScore
                            '    offColorMaxIndex = gemLoopvar
                            'End If
                            '' for hit and exp cap forcing.  These aren't reset
                            '' for each socket
                            'If tGemScore > bestHitGemScore And gems(gemLoopvar).hit > 0 Then
                            '    bestHitGemScore = tGemScore
                            '    bestHitGemIndex = gemLoopvar
                            'End If
                            'If tGemScore > bestExpGemScore And gems(gemLoopvar).exp > 0 Then
                            '    bestExpGemScore = tGemScore
                            '    bestExpGemIndex = gemLoopvar
                            'End If
                        End If
                    Next
                    ' Ok, so now for blue sockets I've got the best scoring gem of each hit / exp combination
                    ' stored in bestgemscore and bestgemindex arrays.  If hit cap isn't forced, then we select
                    ' gems as follows: for hit - if the best 20 hit is > best 20 nonhit, then take the 
                    ' 20 hit, 10 hit, and 20 nonhit.  If it's < 20 nonhit, ignore it and move ot the 10 hit
                    ' if that's > 20 nonhit, take 10 hit and 20 nonhit.
                    ' For exp, similar deal.
                    ' This is for the blue slot, so ignoring capping restrictions, we only take the 10/10 hit /exp
                    ' gem if it is greater than both the 10 hit and 10 exp gems.

                    ' Hit
                    If bestGemScore(2, 0) > bestGemScore(0, 0) Then ' 20 hit gem
                        theMatrix(bestGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True
                    End If
                    If bestGemScore(1, 0) > bestGemScore(0, 0) Then ' 10 hit / 10 noncap gem
                        theMatrix(bestGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True
                    End If
                    If bestGemIndex(0, 0) <> -1 Then
                        theMatrix(bestGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True ' 20 noncap gem
                    End If
                    ' note that the best noncapping stat gem of the appropriate color has no condition
                    ' associated with it.  This will always be good.

                    ' Exp
                    If bestGemScore(0, 2) > bestGemScore(0, 0) Then ' 20 exp gem
                        theMatrix(bestGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.blue) = True
                    End If
                    If bestGemScore(0, 1) > bestGemScore(0, 0) Then ' 10 exp gem
                        theMatrix(bestGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.blue) = True
                    End If

                    ' Exp / Hit
                    If bestGemScore(1, 1) > bestGemScore(1, 0) And bestGemScore(1, 1) > bestGemScore(0, 1) Then '10/10 gem
                        theMatrix(bestGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.blue) = True
                    End If

                    ' Okay, now we've added the best on color gems for each of our hit / exp combinations
                    ' assuming that they were better than the best non-cappable gem. keep track of
                    ' the best overall gem of each category for off-color gemming later.
                    For loopvar = 0 To 2
                        For loopvar2 = 0 To 2
                            If bestGemScore(loopvar, loopvar2) > bestoffGemScore(loopvar, loopvar2) Then
                                bestoffGemScore(loopvar, loopvar2) = bestGemScore(loopvar, loopvar2)
                                bestOffGemIndex(loopvar, loopvar2) = bestGemIndex(loopvar, loopvar2)
                            End If
                        Next
                    Next

                    ' Done with all three socket colors.
                    ' Now we've calculated the best matching gem for all sockets, now we 
                    ' need to calculate the best gem for each socket independent
                    ' of color.  Luckily we've been keeping track in bestOffGemScore
                    ' and bestOffGemIndex.  We need similar logic here for scores, but
                    ' any gem here that could be best for offset has to go in.

                    ' Hit
                    If bestoffGemScore(2, 0) > bestoffGemScore(0, 0) Then ' 20 hit gem
                        theMatrix(bestOffGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(bestOffGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(2, 0), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If
                    If bestoffGemScore(1, 0) > bestoffGemScore(0, 0) Then ' 10 hit / 10 noncap gem
                        theMatrix(bestOffGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(bestOffGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(1, 0), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If
                    If bestoffGemScore(0, 0) <> 0 Then
                        theMatrix(bestOffGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.red) = True ' 20 noncap gem
                        theMatrix(bestOffGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(0, 0), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If
                    If BoldIndex <> -1 Then
                        theMatrix(BoldIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(BoldIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(BoldIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(BoldIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If
                    ' note that the best noncapping stat gem of the appropriate color has no condition
                    ' associated with it.  This will always be good.

                    ' Exp
                    If bestoffGemScore(0, 2) > bestoffGemScore(0, 0) Then ' 20 exp gem
                        theMatrix(bestOffGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(bestOffGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(0, 2), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If
                    If bestoffGemScore(0, 1) > bestoffGemScore(0, 0) Then ' 10 exp gem
                        theMatrix(bestOffGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(bestOffGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(0, 1), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If

                    ' Exp / Hit
                    If bestoffGemScore(1, 1) > bestoffGemScore(0, 0) Then '10/10 gem
                        theMatrix(bestOffGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.red) = True
                        theMatrix(bestOffGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.yellow) = True
                        theMatrix(bestOffGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.blue) = True
                        theMatrix(bestOffGemIndex(1, 1), hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    End If



                    ' Find the best overall that isn't unique
                    ' We've stored this is offcolormax and offcolormaxindex
                    ' that index is valid for all socket colors including prismatic at this
                    ' hit / exp level.
                    'theMatrix(offColorMaxIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    'theMatrix(offColorMaxIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    'theMatrix(offColorMaxIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    'theMatrix(offColorMaxIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True

                    ' The prismatic gem is only valid in yellow, blue, or prismatic sockets, but it is always valid.
                    For gemLoopvar = 0 To numGems - 1
                        If gems(gemLoopvar).color = Gem.colors.prismatic Then
                            'theMatrix(gemLoopvar, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                            theMatrix(gemLoopvar, hitLoopvar, expLoopvar, socketcolor.blue) = True
                            theMatrix(gemLoopvar, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                        End If
                    Next

                    ' Now for situations with force hit and expertise cap.
                    ' We we do is for every situation where hit or exp is less than the 
                    ' desired cap, in addition to all the gems added above we 
                    ' add the best hit / exp gem to all sockets regardless of color.
                    ' If both hit and exp are added, we also add accurate ametrine to all sockets
                    ' in conditions where neither hit or exp are capped yet.
                    'Select Case forceHit
                    '    Case forceHitCap.none
                    '        ' Do nothing
                    '    Case forceHitCap.melee
                    '        If hitLoopvar < meleeHitCap And bestHitGemIndex <> -1 Then
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    '        End If
                    '    Case forceHitCap.spell
                    '        If bestHitGemIndex <> -1 Then
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    '            theMatrix(bestHitGemIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    '        End If
                    'End Select
                    'If forceExp = True And bestExpGemIndex <> -1 Then
                    '    theMatrix(bestExpGemIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    '    theMatrix(bestExpGemIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    '    theMatrix(bestExpGemIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    '    theMatrix(bestExpGemIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    'End If
                    'If forceExp = True And forceHit = forceHitCap.melee And hitLoopvar < meleeHitCap And accurateAmetrineIndex <> -1 Then
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    'End If
                    'If forceExp = True And forceHit = forceHitCap.spell And accurateAmetrineIndex <> -1 Then
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.red) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.yellow) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.blue) = True
                    '    theMatrix(accurateAmetrineIndex, hitLoopvar, expLoopvar, socketcolor.prismatic) = True
                    'End If
                Next
            Next
        Catch ex As Exception
            MessageBox.Show("An unexpected error has occurred. DK Optimize will exit.", "DK Optimize", MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1)
            ErrLogWriter.writeLog(ex)
            End
        End Try
    End Sub

    Public Function fitsStrict(ByVal gemID As Integer, ByVal socketcolorIN As Item.colors, ByVal curHit As Integer, ByVal curExp As Integer) As Boolean
        Try
            Dim tempHit As Integer
            Dim tempExp As Integer
            Dim tempColor As socketcolor

            Select Case socketcolorIN
                Case Item.colors.none
                    ' Well if there is no socket then we can't put a gem in it, can we?
                    fitsStrict = False
                    Exit Function
                Case Item.colors.red
                    tempColor = socketcolor.red
                Case Item.colors.yellow
                    tempColor = socketcolor.yellow
                Case Item.colors.blue
                    tempColor = socketcolor.blue
                Case Item.colors.prismatic
                    tempColor = socketcolor.prismatic
            End Select
            If curExp > maxExp Then
                tempExp = maxExp
            Else
                tempExp = curExp
            End If
            If curHit > maxHit Then
                tempHit = maxHit
            Else
                tempHit = curHit
            End If
            fitsStrict = theMatrix(gemID, tempHit, tempExp, tempColor)
        Catch ex As Threading.ThreadAbortException
        Catch ex As Exception
            MessageBox.Show("An unexpected error has occurred. DK Optimize will exit.", "DK Optimize", MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1)
            ErrLogWriter.writeLog(ex)
            End
        End Try
    End Function
End Class
